////////////////////////////////////////////////////////////////////////////////
// This file is part of the Mist compiler.                                    //
//                                                                            //
// Copyright (C) 2009 The Mist project                                        //
//                                                                            //
// For a list of contributors, see the COMMITTERS file.                       //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modify  //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// in the COPYING file. If not, see <http://www.gnu.org/licenses/>.           //
////////////////////////////////////////////////////////////////////////////////

///
/// \file
/// Contains global AST related function implementations from tree.hpp.
///

#include "tree.hpp"

////////////////////////////////////////////////////////////////////////////////
// Includes                                                                   //
////////////////////////////////////////////////////////////////////////////////

#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH
#define foreach_reverse BOOST_REVERSE_FOREACH

#include "common/numeric.hpp"
#include "parse.hpp"
#include "setparents.hpp"
#include "symbol.hpp"

////////////////////////////////////////////////////////////////////////////////
// Global Function Implementations                                            //
////////////////////////////////////////////////////////////////////////////////

sptr<Declarations> merged_declarations(wptr<Declarations> a, wptr<Declarations> b) {
	sptr<Declarations> result = Declarations::construct();
	
	foreach (sptr<Declaration> d, a->declarations)
		result->declarations.push_back(d);
	
	foreach (sptr<Declaration> d, b->declarations)
		result->declarations.push_back(d);
	
	set_parents(result);
	
	assert(a->scope() == b->scope());
	
	result->scope.set_automatically(a->scope());
	
	return result;
}

bool equal(wptr<Node> a, wptr<Node> b) {
	assert(a);
	assert(b);
	
	// TODO: This is currently done by comparing echo's. This is slow,
	//       and there may be situations in the future for which it
	//       doesn't work right. Fix this function to use a better way.
	
	return (string)echo(a) == (string)echo(b);
}

bool before(wptr<Node> a, wptr<Node> b) {
	assert(a);
	assert(b);
	
	// TODO: This is currently done by comparing echo's. This is slow,
	//       and there may be situations in the future for which it
	//       doesn't work right. Fix this function to use a better way.
	
	return (string)echo(a) < (string)echo(b);
}

bool same_type(wptr<Type> a, wptr<Type> b, map<wptr<Symbol>, wptr<Type> >& translations) {
	assert(a);
	assert(b);
	
	if (typeid(*a) != typeid(*b))
		return false;
	
	if (typeid(*a) == typeid(NamedType)) {
		wptr<Type> at = a, bt = b;
		if (translations.find(a.cast<NamedType>()->reference->symbol()) != translations.end())
			at = translations[a.cast<NamedType>()->reference->symbol()];
		if (translations.find(b.cast<NamedType>()->reference->symbol()) != translations.end())
			bt = translations[b.cast<NamedType>()->reference->symbol()];
		
		if (at == a && bt == b) {
			return a.cast<NamedType>()->reference->symbol() == b.cast<NamedType>()->reference->symbol();
		} else if (at == a && !bt) {
			translations[b.cast<NamedType>()->reference->symbol()] = at;
			return true;
		} else if (bt == b && !at) {
			translations[a.cast<NamedType>()->reference->symbol()] = bt;
			return true;
		} else {
			return same_type(at, bt, translations);
		}
	}
	
	if (typeid(*a) == typeid(FunctionType)) {
		if (!same_type(a.cast<FunctionType>()->returnType, b.cast<FunctionType>()->returnType, translations))
			return false;
		if (a.cast<FunctionType>()->parameterTypes.size() != b.cast<FunctionType>()->parameterTypes.size())
			return false;
		for (uint i = 0; i < a.cast<FunctionType>()->parameterTypes.size(); ++i)
			if (!same_type(a.cast<FunctionType>()->parameterTypes[i], b.cast<FunctionType>()->parameterTypes[i], translations))
				return false;
	}
	
	if (typeid(*a) == typeid(PointerType)) {
		if (a.cast<PointerType>()->strength != b.cast<PointerType>()->strength)
			return false;
		if (!same_type(a.cast<PointerType>()->type, b.cast<PointerType>()->type, translations))
			return false;
	}
	
	if (typeid(*a) == typeid(ArrayType))
		return same_type(a.cast<ArrayType>()->type, b.cast<ArrayType>()->type, translations);
	
	return true;
}

bool same_type(wptr<Type> a, wptr<Type> b) {
	map<wptr<Symbol>, wptr<Type> > translations;
	return same_type(a, b, translations);
}

vector<sptr<Reference> > get_operator_references(wptr<Operator> op, vector<wptr<Expression> > operands, wptr<Symbol> scope) {
	vector<sptr<Reference> > result;
	
	// Make a vector of the composite operator, even if it's a single operator.
	//
	vector<wptr<SingleOperator> > operators;
	if (typeid(*op) == typeid(SingleOperator))
		operators.push_back(op.cast<SingleOperator>());
	else
		operators = op.cast<CompositeOperator>()->operators();
	
	// Find the types of the actual parameters (operands).
	//
	vector<sptr<Type> > types;
	foreach (wptr<Expression> operand, operands)
		types.push_back(operand->type());
	
	foreach_reverse (wptr<SingleOperator> opr, operators) {
		// Find the exact reference belonging to the single operator.
		//
		sptr<Reference> reference = parse<Reference>("." + opr->function_name(), scope);
		reference->formalTypeSpec = types;
		result.insert(result.begin(), reference);
		
		assert(typeid(*reference->symbol()->type) == typeid(FunctionType));
		
		// Find the return type of the associated function.
		//
		types.clear();
		types.push_back(reference->symbol()->type.cast<FunctionType>()->returnType);
	}
	
	return result;
}

map<wptr<Symbol>, sptr<Node> > logical_var_simple_forms(wptr<FunctionDeclaration> decl) {
	map<wptr<Symbol>, sptr<Node> > result;
	
	foreach (wptr<AssertionArea> pre, decl->pre) {
		foreach (wptr<Expression> assertion, pre->conditions) {
			// TODO: This finds only a single simple form as a top-level assertion.
			//       As in: @ l = expr;, logical var left, expression right
			if (typeid(*assertion) == typeid(ChainExpression)) {
			if (assertion.cast<ChainExpression>()->operators.size() == 1) {
			if (typeid(*assertion.cast<ChainExpression>()->operators[0]) == typeid(CompositeOperator)) {
			if (assertion.cast<ChainExpression>()->operators[0].cast<CompositeOperator>()->operators()[0]->function_name() == "equal") {
			if (typeid(*assertion.cast<ChainExpression>()->operands[0]) == typeid(Reference)) {
			if (assertion.cast<ChainExpression>()->operands[0].cast<Reference>()->symbol()->access.logical) {
			    result[assertion.cast<ChainExpression>()->operands[0].cast<Reference>()->symbol()] = assertion.cast<ChainExpression>()->operands[1];
			}}}}}}
		}
	}
	
	return result;
}

bool TypeListOrder::operator()(const vector<wptr<Type> >& a, const vector<wptr<Type> >& b) const {
	for (uint i = 0; i < min(a.size(), b.size()); ++i) {
		if (before(a.at(i), b.at(i)))
			return true;
		if (before(b.at(i), a.at(i)))
			return false;
	}
	if (a.size() < b.size())
		return true;
	return false;
}

bool TypeListOrder::operator()(const vector<sptr<Type> >& a, const vector<sptr<Type> >& b) const {
	return (*this)(weak_vector(a), weak_vector(b));
}
